🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / ble / src / androidHostTest / kotlin / org / meshtastic / core / ble / AndroidBluetoothRepositoryBondTest.kt
Displaying Raw • Download
core/ble/src/androidHostTest/kotlin/org/meshtastic/core/ble/AndroidBluetoothRepositoryBondTest.kt b4bedd92fcc8d65daff79200d71f6ebc3d8f0cfa (b4bedd92) Text, 21.14 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.ble
Tff7b72import T7ee787android.bluetooth.BluetoothAdapter
Tff7b72import T7ee787android.bluetooth.BluetoothDevice
Tff7b72import T7ee787androidx.lifecycle.Lifecycle
Tff7b72import T7ee787androidx.lifecycle.LifecycleOwner
Tff7b72import T7ee787androidx.lifecycle.LifecycleRegistry
Tff7b72import T7ee787kotlinx.coroutines.CompletableDeferred
Tff7b72import T7ee787kotlinx.coroutines.Deferred
Tff7b72import T7ee787kotlinx.coroutines.ExperimentalCoroutinesApi
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787kotlinx.coroutines.test.TestDispatcher
Tff7b72import T7ee787kotlinx.coroutines.test.TestScope
Tff7b72import T7ee787kotlinx.coroutines.test.UnconfinedTestDispatcher
Tff7b72import T7ee787kotlinx.coroutines.test.advanceTimeBy
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787org.junit.runner.RunWith
Tff7b72import T7ee787org.meshtastic.core.di.CoroutineDispatchers
Tff7b72import T7ee787org.meshtastic.core.testing.FakeBleDevice
Tff7b72import T7ee787org.meshtastic.core.testing.RobolectricBleBonding
Tff7b72import T7ee787org.robolectric.RobolectricTestRunner
Tff7b72import T7ee787org.robolectric.RuntimeEnvironment
Tff7b72import T7ee787org.robolectric.Shadows.shadowOf
Tff7b72import T7ee787org.robolectric.annotation.Config
Tff7b72import T7ee787org.robolectric.annotation.Implementation
Tff7b72import T7ee787org.robolectric.annotation.Implements
Tff7b72import T7ee787org.robolectric.shadows.ShadowBluetoothDevice
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertFailsWith
Tff7b72import T7ee787kotlin.test.assertFalse
Tff7b72import T7ee787kotlin.test.assertNull
Tff7b72import T7ee787kotlin.test.assertTrue
T8b949e/**
* Coverage for the #5849 fix in [AndroidBluetoothRepository.bond]: when `createBond()` returns false the flow no longer
* fails outright — it re-checks `bondState` and either resumes (already BONDED), keeps waiting on the broadcast
* receiver (still BONDING), or fails ("Failed to initiate bonding").
*
* Exercises the real production method via Robolectric shadows (no production seam): [RobolectricBleBonding] drives the
* address-cached [ShadowBluetoothDevice] and fires `ACTION_BOND_STATE_CHANGED` broadcasts. Each test uses a distinct
* MAC so the static device cache cannot bleed bond-state across tests.
*/
Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalCoroutinesApiTff7b72::Te6edf3classTb4b4b4)
Tf0883e@RunWithTb4b4b4(Te6edf3RobolectricTestRunnerTff7b72::Te6edf3classTb4b4b4)
Tf0883e@ConfigTb4b4b4(Te6edf3sdk Tff7b72= Tff7b72[T79c0ff3T79c0ff4Tff7b72]Tb4b4b4)
Tff7b72class T56d364AndroidBluetoothRepositoryBondTest Tb4b4b4{
Tff7b72private Tff7b72fun Td2a8ffnewRepositoryTb4b4b4(Te6edf3dispatcherTb4b4b4: Te6edf3TestDispatcherTb4b4b4)Tb4b4b4: Te6edf3AndroidBluetoothRepository Tb4b4b4{
Tff7b72val Te6edf3context Tff7b72= Te6edf3RuntimeEnvironmentTb4b4b4.Te6edf3getApplicationTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3dispatchers Tff7b72= Te6edf3CoroutineDispatchersTb4b4b4(Te6edf3io Tff7b72= Te6edf3dispatcherTb4b4b4, Te6edf3main Tff7b72= Te6edf3dispatcherTb4b4b4, Te6edf3default Tff7b72= Te6edf3dispatcherTb4b4b4)
Tff7b72return Te6edf3AndroidBluetoothRepositoryTb4b4b4(Te6edf3contextTb4b4b4, Te6edf3dispatchersTb4b4b4, Te6edf3startedLifecycleTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
T8b949e/** A minimal STARTED lifecycle so the repository's `init { ... }` launch has a live, non-cancelled scope. */
Tff7b72private Tff7b72fun Td2a8ffstartedLifecycleTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3Lifecycle Tb4b4b4{
Tff7b72val Te6edf3owner Tff7b72=
Tff7b72object Tb4b4b4: T56d364LifecycleOwner Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3lifecycleTb4b4b4: Te6edf3LifecycleRegistry Tff7b72=
Te6edf3LifecycleRegistryTb4b4b4.Te6edf3createUnsafeTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3apply Tb4b4b4{ Te6edf3currentState Tff7b72= Te6edf3LifecycleTb4b4b4.Te6edf3StateTb4b4b4.Te6edf3STARTED Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3ownerTb4b4b4.Te6edf3lifecycle
Tb4b4b4}
T8b949e/**
* Launch `bond()` in the background so it can suspend (e.g. in the BONDING branch) while the test fires the
* resolving broadcast. The returned [Deferred] completes with the throwable `bond()` raised, or `null` on success.
* Using launch + captured result avoids `async`/`await` exception-propagation surprises under the test scope.
*/
Tff7b72private Tff7b72fun Te6edf3TestScopeTb4b4b4.Td2a8fflaunchBondTb4b4b4(Te6edf3repoTb4b4b4: Te6edf3AndroidBluetoothRepositoryTb4b4b4, Te6edf3macTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3DeferredTff7b72<Te6edf3Throwable?Tff7b72> Tb4b4b4{
Tff7b72val Te6edf3result Tff7b72= Te6edf3CompletableDeferredTff7b72<Te6edf3Throwable?Tff7b72>Tb4b4b4(Tb4b4b4)
Te6edf3backgroundScopeTb4b4b4.Te6edf3launch Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3completeTb4b4b4(Te6edf3runCatching Tb4b4b4{ Te6edf3repoTb4b4b4.Te6edf3bondTb4b4b4(Te6edf3FakeBleDeviceTb4b4b4(Te6edf3address Tff7b72= Te6edf3macTb4b4b4)Tb4b4b4) Tb4b4b4}Tb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3result
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`createBond false while still BONDING keeps waiting and resumes on a later BONDED broadcast`Tb4b4b4(Tb4b4b4) Tff7b72=
Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:01Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
T8b949e// bond() runs until it parks in the BONDING branch (receiver left registered).
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
T8b949e// The OS later completes bonding — the broadcast must resume the suspended call.
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3sendBondStateChangedTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3newState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4,
Te6edf3previousState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4,
Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffbond() should have resumed without an errorTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`createBond false with no in-flight bond fails to initiate bonding`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:02Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3error Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3ExceptionTff7b72> Tb4b4b4{ Te6edf3repoTb4b4b4.Te6edf3bondTb4b4b4(Te6edf3FakeBleDeviceTb4b4b4(Te6edf3address Tff7b72= Te6edf3macTb4b4b4)Tb4b4b4) Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffFailed to initiate bondingTa5d6ff"Tb4b4b4, Te6edf3errorTb4b4b4.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tf0883e@ConfigTb4b4b4(Te6edf3sdk Tff7b72= Tff7b72[T79c0ff3T79c0ff4Tff7b72]Tb4b4b4, Te6edf3shadows Tff7b72= Tff7b72[Te6edf3ShadowBondingThenBondedTff7b72::Te6edf3classTff7b72]Tb4b4b4)
Tff7b72fun Td2a8ff`post receiver registration bond recheck resumes without createBond`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// Models the real-device race where the bond completes between the early bondState check and receiver
T8b949e// registration. The custom shadow returns BONDING first, then BONDED, so the registered receiver is cleaned
T8b949e// up before createBond() is called.
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:03Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Te6edf3ShadowBondingThenBondedTb4b4b4.Te6edf3createBondCalls Tff7b72= T79c0ff0
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
T8b949e// Resumes (no broadcast) via the post-registration BOND_BONDED re-check; assert no error surfaced.
Te6edf3assertNullTb4b4b4(Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)Tb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffbond() should resume when the re-check finds BOND_BONDEDTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff0Tb4b4b4, Te6edf3ShadowBondingThenBondedTb4b4b4.Te6edf3createBondCallsTb4b4b4, Ta5d6ff"Ta5d6ffcreateBond() should not run after the re-checkTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`already bonded device returns immediately without initiating a bond`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:04Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
T8b949e// Make the device already bonded both at the adapter level (so isBonded is observable) and per-device
T8b949e// (so bond() hits the early BOND_BONDED guard at line 85 without ever calling createBond()).
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4)
Te6edf3shadowOfTb4b4b4(Te6edf3BluetoothAdapterTb4b4b4.Te6edf3getDefaultAdapterTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4.Te6edf3setBondedDevicesTb4b4b4(Te6edf3setOfTb4b4b4(Te6edf3BluetoothAdapterTb4b4b4.Te6edf3getDefaultAdapterTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3getRemoteDeviceTb4b4b4(Te6edf3macTb4b4b4)Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)Tb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffan already-bonded device should return without errorTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3repoTb4b4b4.Te6edf3isBondedTb4b4b4(Te6edf3macTb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffthe device should remain reported as bondedTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`bond fails when bonding is rejected (BOND_NONE from BONDING)`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:05Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3sendBondStateChangedTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3newState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4,
Te6edf3previousState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4,
Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3BOND_FAILED_OR_REJECTED_MESSAGETb4b4b4, Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`createBond true then a BONDED broadcast resumes the bond`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// The ordinary successful path: createBond() succeeds, the call parks on the receiver, and the OS later
T8b949e// confirms via ACTION_BOND_STATE_CHANGED(BOND_BONDED).
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:08Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72trueTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3sendBondStateChangedTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3newState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4,
Te6edf3previousState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4,
Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffa freshly initiated bond should resolve on the BONDED broadcastTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`a BOND_NONE broadcast from a non-BONDING state does not fail the parked bond`Tb4b4b4(Tb4b4b4) Tff7b72=
Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// Only BOND_NONE *from* BOND_BONDING means rejection; a spurious BOND_NONE (prev != BONDING) must be
T8b949e// ignored and leave the call waiting.
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:09Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3sendBondStateChangedTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3newState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4,
Te6edf3previousState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4,
Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffa spurious BOND_NONE must not resolve the bondTa5d6ff"Tb4b4b4)
T8b949e// A genuine completion still resolves it (and cleans up the background coroutine).
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3sendBondStateChangedTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3newState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4,
Te6edf3previousState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4,
Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`bond times out when no terminal bond broadcast arrives`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:0ATa5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff2T79c0ff9Te6edf3_999LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffbond() should still be waiting before the timeoutTa5d6ff"Tb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff2LTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffTimed out waiting for bonding to completeTa5d6ff"Tb4b4b4, Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`bond completes early when bond state becomes bonded without broadcast`Tb4b4b4(Tb4b4b4) Tff7b72=
Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:0BTa5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3deviceShadow Tff7b72=
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4,
Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff4T79c0ff9T79c0ff9LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffbond() should still be waiting before the poll intervalTa5d6ff"Tb4b4b4)
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff2LTb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffbond() should accept the polled BONDED state before timeoutTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`bond fails early when bond state returns none without broadcast`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:0DTa5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3deviceShadow Tff7b72=
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4,
Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff4T79c0ff9T79c0ff9LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffbond() should still be waiting before the poll intervalTa5d6ff"Tb4b4b4)
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff2LTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3BOND_FAILED_OR_REJECTED_MESSAGETb4b4b4, Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`createBond true does not fail immediately while bond state is initially none`Tb4b4b4(Tb4b4b4) Tff7b72=
Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:0ETa5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3deviceShadow Tff7b72=
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72trueTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
T8b949e// Robolectric synchronously marks createBond() as BONDED here; reset it to model
T8b949e// Android's async BOND_NONE -> BOND_BONDING transition.
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff4T79c0ff9T79c0ff9LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffbond() should still be waiting before the initial grace pollTa5d6ff"Tb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff2LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffa newly initiated bond should tolerate initial BOND_NONETa5d6ff"Tb4b4b4)
T8b949e// The same wait must still recover if Android later reports BONDING and then BONDED.
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5T79c0ff0T79c0ff0LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffBOND_BONDING should keep the bond wait activeTa5d6ff"Tb4b4b4)
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5T79c0ff0T79c0ff0LTb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffbond() should complete once Android reports BOND_BONDEDTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`createBond true fails on the BOND_NONE poll after grace is exhausted`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:0FTa5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3deviceShadow Tff7b72=
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72trueTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
T8b949e// Robolectric synchronously marks createBond() as BONDED here; reset it to model
T8b949e// Android's async BOND_NONE -> BOND_BONDING transition.
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff4T79c0ff9T79c0ff9LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffbond() should still be waiting before the initial grace pollTa5d6ff"Tb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff2LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffthe first BOND_NONE poll should still be within the graceTa5d6ff"Tb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5T79c0ff0T79c0ff0LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffthe second BOND_NONE poll should consume the graceTa5d6ff"Tb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5T79c0ff0T79c0ff0LTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3BOND_FAILED_OR_REJECTED_MESSAGETb4b4b4, Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`createBond true fails when polled bonding later returns none`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:10Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3deviceShadow Tff7b72=
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(Te6edf3macTb4b4b4, Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4, Te6edf3createBondReturns Tff7b72= Tff7b72trueTb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
T8b949e// Robolectric synchronously marks createBond() as BONDED here; reset it to model
T8b949e// Android's async BOND_NONE -> BOND_BONDING transition.
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5T79c0ff0T79c0ff1LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffinitial BOND_NONE should not fail before bonding is observedTa5d6ff"Tb4b4b4)
T8b949e// Covers the missed failure broadcast after polling observes BOND_BONDING. The createBond=false
T8b949e// BOND_BONDING test pins the already-in-flight start path.
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5T79c0ff0T79c0ff0LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffpolled BOND_BONDING should keep the bond wait activeTa5d6ff"Tb4b4b4)
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5T79c0ff0T79c0ff0LTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3BOND_FAILED_OR_REJECTED_MESSAGETb4b4b4, Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`spurious none broadcast is ignored but later polled none fails`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:11Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3deviceShadow Tff7b72=
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4,
Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3sendBondStateChangedTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3newState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4,
Te6edf3previousState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4,
Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffa spurious BOND_NONE broadcast must not resolve the bondTa5d6ff"Tb4b4b4)
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff5T79c0ff0T79c0ff0LTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3BOND_FAILED_OR_REJECTED_MESSAGETb4b4b4, Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`bond succeeds when bond state becomes bonded at timeout boundary`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3mac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:0CTa5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3deviceShadow Tff7b72=
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3primeBondTb4b4b4(
Te6edf3macTb4b4b4,
Te6edf3bondState Tff7b72= Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDINGTb4b4b4,
Te6edf3createBondReturns Tff7b72= Tff7b72falseTb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3failure Tff7b72= Te6edf3launchBondTb4b4b4(Te6edf3repoTb4b4b4, Te6edf3macTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff2T79c0ff9Te6edf3_999LTb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3isCompletedTb4b4b4, Ta5d6ff"Ta5d6ffbond() should still be waiting before the timeoutTa5d6ff"Tb4b4b4)
Te6edf3deviceShadowTb4b4b4.Te6edf3setBondStateTb4b4b4(Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4)
Te6edf3advanceTimeByTb4b4b4(T79c0ff2LTb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3failureTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffbond() should accept the final BONDED state after timeoutTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`isBonded reflects the adapter bonded devices`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3bondedMac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:06Ta5d6ff"
Tff7b72val Te6edf3otherMac Tff7b72= Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:07Ta5d6ff"
Te6edf3RobolectricBleBondingTb4b4b4.Te6edf3grantBluetoothConnectPermissionTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bondedDevice Tff7b72= Te6edf3BluetoothAdapterTb4b4b4.Te6edf3getDefaultAdapterTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3getRemoteDeviceTb4b4b4(Te6edf3bondedMacTb4b4b4)
Te6edf3shadowOfTb4b4b4(Te6edf3BluetoothAdapterTb4b4b4.Te6edf3getDefaultAdapterTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4.Te6edf3setBondedDevicesTb4b4b4(Te6edf3setOfTb4b4b4(Te6edf3bondedDeviceTb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3repoTb4b4b4.Te6edf3isBondedTb4b4b4(Te6edf3bondedMacTb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3repoTb4b4b4.Te6edf3isBondedTb4b4b4(Te6edf3otherMacTb4b4b4)Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`isValid accepts a well-formed MAC and rejects garbage`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3repo Tff7b72= Te6edf3newRepositoryTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3testSchedulerTb4b4b4)Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3repoTb4b4b4.Te6edf3isValidTb4b4b4(Ta5d6ff"Ta5d6ffAA:BB:CC:DD:EE:FFTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3repoTb4b4b4.Te6edf3isValidTb4b4b4(Ta5d6ff"Ta5d6ffnot-a-macTa5d6ff"Tb4b4b4)Tb4b4b4)
Tb4b4b4}
T8b949e/**
* Custom shadow that returns [BluetoothDevice.BOND_BONDING] on the first `getBondState()` read (the early guard)
* and [BluetoothDevice.BOND_BONDED] thereafter (the post-receiver-registration re-check), reproducing the
* bond-completed-mid-method race without a broadcast.
*/
Tf0883e@ImplementsTb4b4b4(Te6edf3BluetoothDeviceTff7b72::Te6edf3classTb4b4b4)
Tff7b72class T56d364ShadowBondingThenBonded Tb4b4b4: Te6edf3ShadowBluetoothDeviceTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72companion Tff7b72object Tb4b4b4{
Tff7b72var Te6edf3createBondCallsTb4b4b4: Tffa657Int Tff7b72= T79c0ff0
Tb4b4b4}
Tff7b72private Tff7b72var Te6edf3bondStateReads Tff7b72= T79c0ff0
Tf0883e@Implementation
Tff7b72override Tff7b72fun Td2a8ffgetBondStateTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Int Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3bondStateReadsTff7b72+Tff7b72+ Tff7b72=Tff7b72= T79c0ff0Tb4b4b4) Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDING Tff7b72else Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDED
Tf0883e@Implementation
Tff7b72override Tff7b72fun Td2a8ffcreateBondTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Te6edf3createBondCallsTff7b72+Tff7b72+
Tff7b72return Tff7b72false
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.23s